<?php
// +-------------------------------------------------------------------
// | 
// +-------------------------------------------------------------------
// | Copyright (c) 2009-2016 All rights reserved.
// +-------------------------------------------------------------------
namespace Kcdns\Admin\Common;

class ModelCfg
{
    // 字段分隔符, 代替点号, 避免 get/post 键名中的点号被 PHP 替换为 下划线
    const ALIAS_SP = '__CFG__';

    public $name;

    public $table;

    // 表单环境 add / edit
    public $env;

    public $configOri;

    // 列表 : 搜索配置
    public $listSearch = [];

    // 列表 : 连表配置
    public $joinTable = [];

    // 列表 : 列配置
    public $listGrid = [];

    // 列表 : 操作配置
    public $listOpration = [];

    // 列表 : 操作列宽度
    public $listOprationWidth = 50;

    // 列表 : 按钮配置
    public $listBtutton = [];

    // 表单 : 字段配置
    public $formField = [];

    // 表单 : 按钮配置
    public $formButton = [];

    // 所有可用字段列表
    protected $availableFields = [];

    protected static $instance = [];

    public function __construct($name = null, $env = '', $returnBlank = false)
    {
        $contorllerName = preg_replace("/^(Z[A-Z])/", "", CONTROLLER_NAME);
        $searchNames = $name ? [
            $name
        ] : [
            $contorllerName . '/' . ACTION_NAME,
            $contorllerName
        ];

        $configJson = null;
        foreach ($searchNames as $_name) {
            $_name = strtolower(parse_name($_name));
            $configJson = json_decode(C("MODEL_CFG_" . $_name), true);
            if ($configJson) {
                $this->name = strtolower($_name);
                break;
            }
        }

        $returnBlank and !$configJson and $configJson = [];
        if ($configJson === null) {
            throw new \Exception('Model cfg not found [' . implode(',', $searchNames) . ']');
        }

        // 通过控制器名称检测当前环境
        if (!$env) {
            strpos(ACTION_NAME, 'add') !== false and $env = 'add';
            strpos(ACTION_NAME, 'edit') !== false and $env = 'edit';
        }

        // 计算表名
        $_nameArr = explode('/', $_name);
        $this->table = $_nameArr[0];
        $this->configOri = $configJson;
        $this->env = $env;
        $this->_loadCfg();
        $this->_loadModel();
    }

    // 保存配置
    public function save($name, $cfgJsonString)
    {
        filter($name, 'required');
        filter($cfgJsonString, 'required');

        $name = strtolower($name);
        $configName = "MODEL_CFG_{$name}";
        $where = [
            'name' => $configName
        ];
        $record = $configJsonString = M('config')->where($where)->find();

        return $record ? M('config')->where([
                'name' => $configName
            ])->data([
                'value' => $cfgJsonString,
                'update_time' => date('Y-m-d H:i:s'),
            ])->save() !== false : M('config')->data([
            'type' => 0,
            'group' => 'model',
            'create_time' => date('Y-m-d H:i:s'),
            'update_time' => date('Y-m-d H:i:s'),
            'value' => $cfgJsonString,
            'status' => 1,
            'name' => $configName
        ])->add();
    }

    // 格式化配置
    // TODO : 所有配置项递归 trim
    protected function _loadCfg()
    {
        // JOIN TABLE
        $this->joinTable = [];
        $this->configOri['join_table'] = $this->configOri['join_table'] ?: [];
        foreach ($this->configOri['join_table'] as $k => $v) {
            if ($v['table'] && $v['on']) {
                $this->joinTable[] = $v;
            }
        }

        // LIST GRID
        $this->listGrid = [];
        $this->configOri['list_grid'] = $this->configOri['list_grid'] ?: [];
        foreach ($this->configOri['list_grid'] as $k => $v) {
            if (!$v['field']) {
                continue;
            }
            // TODO : 从字段注释中读取默认值
            $v['label'] = $v['label'] ?: strtoupper($v['field']);
            $v['table'] = $v['table'] ?: $this->table;

            $v['value_dic'] = false;
            $v['value_callback'] = false;
            $v['value_callback_param'] = [];

            if ($v['value_format']) {
                if (is_array($v['value_format'])) {
                    $v['value_dic'] = $v['value_format'];
                } else {
                    //支持 系统配置 [ENABLE_STATUS]
                    if (preg_match('/^\[(.+)\]$/', $v['value_format'], $match)) {
                        $cfg_val = C($match[1]);
                        if (is_string($cfg_val) && $cfg_arr = json_decode($cfg_val, true)) {
                            $v['value_dic'] = [];
                            foreach ($cfg_arr as $arr_) {
                                list($k_, $v_) = array_values($arr_);
                                $v['value_dic'][$k_] = $v_;
                            }
                        } else {
                            $v['value_dic'] = $cfg_val;
                        }
                    }

                    // : 开头代表以函数返回值作为字典
                    // 函数参数替换: [row] 替换为当前行, [member.uid] 替换为字段值
                    if (preg_match("/^\s*(:)?([a-zA-Z0-9_]+)\s*\((.*)\)\s*$/", $v['value_format'], $match)) {
                        $callback = $match[2];
                        $param = $match[3] ? explode(',', $match[3]) : [];

                        // 清除参数两端的空白和引号
                        foreach ($param as &$_pv) {
                            $_pv = trim(trim($_pv, '\'"'));
                        }

                        // 字典回调函数
                        if ($match[1]) {
                            $v['value_dic'] = call_user_func_array($callback, $param);
                        } // 值修饰回调函数
                        else {
                            $v['value_callback'] = $callback;
                            $v['value_callback_param'] = $param;
                        }
                    } else if (is_callable($v['value_format'])) {
                        $v['value_callback'] = $v['value_format'];
                        $v['value_callback_param'] = ["[{$v['field']}]"];
                    }
                }
            }
            $v['search_filter'] = $v['search_filter'] ?: 'text';
            $this->listGrid[$v['field']] = $v;
        }

        // LIST OPRATION
        $this->listOpration = $this->configOri['list_opration'] ?: [];
        $len = 0;
        foreach ($this->listOpration as $v) {
            $len += mb_strlen($v['label'], 'UTF-8') * 15;
        }
        $len += count($this->listOpration);
        $this->listOprationWidth = max($len, 50);

        // LIST BUTTON
        $this->listBtutton = $this->configOri['list_button'] ?: [];

        // FORM FIELD
        $this->formField = [];
        $this->configOri['form_field'] = $this->configOri['form_field'] ?: [];
        foreach ($this->configOri['form_field'] as $v) {
            $show = true;
            $read = false;
            switch ($v['is_show']) {
                case 'all-read':
                    $read = true;
                    break;
                case 'add-show':
                    $show = $this->env == 'add';
                    break;
                case 'add-read':
                    $read = $this->env == 'add';
                    break;
                case 'edit-show':
                    $show = $this->env == 'edit';
                    break;
                case 'edit-read':
                    $read = $this->env == 'edit';
                    break;
            }

            // view 模式显示所有字段
            if ($this->env == 'view') {
                $show = true;
                $read = true;
            }

            // 剔除不显示的字段
            if (!$show) {
                continue;
            }

            $v['value_callback'] = false;

            //支持 系统配置 [ENABLE_STATUS]
            if (preg_match('/^\[(.+)\]$/', (string)$v['value_dic'], $match)) {
                $cfg_val = C($match[1]);
                if (is_string($cfg_val) && $cfg_arr = json_decode($cfg_val, true)) {
                    $v['value_dic'] = [];
                    foreach ($cfg_arr as $arr_) {
                        list($k_, $v_) = array_values($arr_);
                        $v['value_dic'][$k_] = $v_;
                    }
                } else {
                    $v['value_dic'] = $cfg_val;
                }
            }

            if ($v['value_dic']) {
                $valueDic = $v['value_dic'];
                if (is_array($valueDic)) {
                    $v['value_dic'] = $valueDic;
                } elseif (is_string($v['value_dic'])) {
                    // : 开头代表以函数返回值作为字典
                    if (preg_match("/^\s*(:)?([a-zA-Z0-9_]+)\s*\((.*)\)\s*$/", $v['value_dic'], $match)) {
                        $callback = $match[2];
                        $param = $match[3] ? explode(',', $match[3]) : [];

                        // 字典回调函数
                        if ($match[1]) {
                            $v['value_dic'] = call_user_func_array($callback, $param);
                        } // 值修饰回调函数
                        else {
                            $v['value_callback'] = $callback;
                            $v['value_callback_param'] = $param;
                        }
                    } else if (is_callable($v['value_dic'])) {
                        $v['value_dic'] = call_user_func_array($v['value_dic'], []);
                    }
                }
            }
            $v['value_dic'] = is_array($v['value_dic']) ? $v['value_dic'] : false;

            $v['rule'] .= ";label={$v['title']}";
            $v['readonly'] = $read;

            $this->formField[$v['name']] = $v;
        }

        // FORM BUTTON
        $this->formButton = $this->configOri['form_button'];

        // 查询所有可用字段, 用于排除非数据库字段的查询
        $this->availableFields = [];
        foreach (array_merge([
            [
                'table' => $this->table
            ]
        ], $this->joinTable) as $v) {
            $this->availableFields[$v['table']] = M($v['table'])->getDbFields();
        }
    }

    // 加载 model
    protected function _loadModel()
    {
        $model = D($this->table);
        $this->model = get_class($model) == 'Think\Model' ? D('Common')->setTable($this->table) : $model;
        $this->model->cfg = $this;
    }

    // 获取列表请求参数
    public function getRequest()
    {
        return W('datatable/request', [
            $this
        ]);
    }

    // 计算列表查询选项
    public function getListModelOptions()
    {
        // 字段列表, 字段全部使用 [表名.字段名]
        $fieldArr = [];
        foreach ($this->listGrid as $alias => $v) {
            list ($_table, $_field) = explode('.', $v['field']);
            $fieldArr[] = "`{$_table}`.`{$_field}` as `{$alias}`";
        }
        $field = implode(',', $fieldArr);

        // 连接表
        $join = "";
        $tables = [];
        foreach ($this->joinTable as $v) {
            if (isset($tables[$v['table']])) {
                $tables[$v['table']] += 1;
                $count = $tables[$v['table']];
            } else {
                $count = '';
                $tables[$v['table']] = 1;
            }

            $join .= "LEFT JOIN `__" . strtoupper($v['table']) . "__` `{$v['table']}$count` ON {$v['on']} ";
        }

        return compact("field", "join");
    }

    // 获取列表值修饰回调函数配置
    public function formatListField($alias, $value, $row)
    {
        $this->listGrid[$alias]['value_format'];
        return $this->listGrid[$alias]['value_format'];
    }

    // 获取列表值翻译字典配置
    public function getListValueFormatDic($alias)
    {
        return $this->listGrid[$alias]['value_dic'];
    }

    // 获取表单查询选项
    public function getFormModelOptions()
    {
        // 字段列表, 字段全部使用别名标识 [表名.字段名]
        $fieldArr = [];
        foreach ($this->formField as $alias => $v) {
            list ($_table, $_field) = explode('.', $v['name']);
            if (isset($this->availableFields[$_table]) && in_array($_field, $this->availableFields[$_table])) {
                $fieldArr[] = "`{$_table}`.`{$_field}` as `{$alias}`";
            }
        }
        $field = implode(',', $fieldArr);

        // 连接表
        $join = "";
        $tables = [];
        foreach ($this->joinTable as $v) {
            if (isset($tables[$v['table']])) {
                $tables[$v['table']] += 1;
                $count = $tables[$v['table']];
            } else {
                $count = '';
                $tables[$v['table']] = 1;
            }
            $join .= "LEFT JOIN `__" . strtoupper($v['table']) . "__` `{$v['table']}$count` ON {$v['on']} ";
        }

        return compact("field", "join");
    }

    // 转义字段名
    public static function escape($fieldAlias)
    {
        return str_replace('.', self::ALIAS_SP, $fieldAlias);
    }

    /**
     * 恢复 get/post/request 中转义的字段名
     * 修正 get/post/request 中的 __NO_CHOOSE__
     */
    public static function clearGP()
    {
        $get = [];
        $post = [];
        $request = [];

        foreach ($_GET as $k => $v) {
            $v === '__NO_CHOOSE__' and $v = '';
            $get[str_replace(self::ALIAS_SP, '.', $k)] = $v;
        }
        foreach ($_POST as $k => $v) {
            $v === '__NO_CHOOSE__' and $v = '';
            $post[str_replace(self::ALIAS_SP, '.', $k)] = $v;
        }
        foreach ($_REQUEST as $k => $v) {
            $v === '__NO_CHOOSE__' and $v = '';
            $request[str_replace(self::ALIAS_SP, '.', $k)] = $v;
        }

        $_GET = $get;
        $_POST = $post;
        $_REQUEST = $request;
    }
}